package cn.italkcloud.cph.helper;

import cn.italkcloud.cph.constants.enums.vos.ApiEnum;
import cn.italkcloud.cph.constants.enums.vos.SqlEnum;
import cn.italkcloud.cph.entity.VosServer;
import cn.italkcloud.cph.entity.dto.vos.*;
import cn.italkcloud.cph.entity.wrapper.VosWrapper;
import cn.italkcloud.cph.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;

/**
 * VOS 工具
 *
 * @author dominate
 * @since 2024/7/8
 **/
@Slf4j
public class VosHelper {

    private static final int MAX_QUERY_DAY = 100;

    private static final int SUCCESS_RET_CODE = 0;
    private static final String TIME_FORMAT = "yyyyMMddHHmmss";
    private static final String DAY_FORMAT = "yyyyMMdd";

    private static final Map<String, String> BASE_HEADER = new HashMap<>();

    static {
        BASE_HEADER.put("Content-Type", "text/html;charset=UTF-8");
    }

    public static boolean checkServerApiConnection(String apiUrl) {
        try {
            String response = request(apiUrl, ApiEnum.GET_CUSTOMER_LIST, "0");
            CustomerListDTO result = JsonUtil.parse(response, CustomerListDTO.class);
            return result.getRetCode() == SUCCESS_RET_CODE;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean checkServerMysqlConnection(String url, String user, String password) {
        try (Connection connection = DataSourceUtil.connect(url, user, password)) {
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(SqlEnum.CHECK_CONNECTION.getSql());
            return rs.next();
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 获取所有账号列表
     *
     * @param apiUrl VOS接口地址
     * @return 账号列表
     */
    public static List<String> getAllCustomerNameList(String apiUrl) {
        // 列表类型 0:获取 accounts 1:获取 infoCustomerBriefs
        String response = request(apiUrl, ApiEnum.GET_CUSTOMER_LIST, "0");
        CustomerListDTO result = JsonUtil.parse(response, CustomerListDTO.class);
        if (result.getRetCode() != SUCCESS_RET_CODE) {
            return Collections.emptyList();
        }
        return result.getAccounts();
    }

    /**
     * 获取账号明细列表
     *
     * @param apiUrl   VOS接口地址
     * @param accounts 账号列表
     * @return 账号明细列表
     */
    public static List<CustomerDetailDTO> getCustomerDetailList(String apiUrl, String... accounts) {
        String response = request(apiUrl, ApiEnum.GET_CUSTOMER_DETAIL, accounts, null, null, null);
        CustomerDetailDTO.Result result = JsonUtil.parse(response, CustomerDetailDTO.Result.class);
        if (result.getRetCode() != SUCCESS_RET_CODE) {
            return Collections.emptyList();
        }
        for (CustomerDetailDTO customerDetail : result.getInfoCustomers()) {
            customerDetail.setMoney(unify(customerDetail.getMoney()));
            customerDetail.setLimitMoney(unify(customerDetail.getLimitMoney()));
            customerDetail.setTodayConsumption(unify(customerDetail.getTodayConsumption()));
        }
        return result.getInfoCustomers();
    }


    /**
     * 获取账号明细
     *
     * @param apiUrl  VOS接口地址
     * @param account 账号
     * @return 账号明细
     */
    public static CustomerDetailDTO getCustomerDetail(String apiUrl, String account) {
        List<CustomerDetailDTO> customerDetailList = getCustomerDetailList(apiUrl, account);
        return customerDetailList.get(0);
    }

    /**
     * 获取月度报表
     *
     * @param apiUrl  VOS接口地址
     * @param month   月
     * @param account 账号
     * @return 费用报表
     */
    public static ReportCustomerFeeDTO getMonthCustomerReport(String apiUrl, Date month, String account) {
        Date beginDate = DateUtil.getMonthFirstDay(month);
        Date endDate = DateUtil.getMonthLastDay(month);
        List<ReportCustomerFeeDTO> customerFeeList = getCustomerReport(apiUrl, beginDate, endDate, account);
        if (CollectionUtils.isEmpty(customerFeeList)) {
            return new ReportCustomerFeeDTO(account, beginDate, endDate);
        }
        return VosWrapper.builder().merge(customerFeeList);
    }

    /**
     * 账号费用报表
     *
     * @param apiUrl   VOS接口地址
     * @param beginDay 开始日期
     * @param endDay   结束日期
     * @param accounts 账号列表
     * @return 费用报表
     */
    public static List<ReportCustomerFeeDTO> getCustomerReport(String apiUrl, Date beginDay, Date endDay, String... accounts) {
        // 统计周期 1:按天 -2:按月
        String response = request(apiUrl, ApiEnum.GET_REPORT_CUSTOMER_FEE, accounts, 1,
                DateUtil.dateToFormatStr(beginDay, DAY_FORMAT), DateUtil.dateToFormatStr(endDay, DAY_FORMAT));
        ReportCustomerFeeDTO.Result result = JsonUtil.parse(response, ReportCustomerFeeDTO.Result.class);
        if (result.getRetCode() != SUCCESS_RET_CODE) {
            return Collections.emptyList();
        }
        for (ReportCustomerFeeDTO reportCustomerFee : result.getInfoReportCustomerFees()) {
            reportCustomerFee.setTotalFee(unify(reportCustomerFee.getTotalFee()));
        }
        return result.getInfoReportCustomerFees();
    }

    /**
     * 获取充值历史
     *
     * @param apiUrl    VOS接口地址
     * @param account   账号
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 充值历史列表
     */
    public static List<PayHistoryDTO> getPayHistory(String apiUrl, String account, Date startTime, Date endTime) {
        String response = request(apiUrl, ApiEnum.GET_PAY_HISTORY, account, null,
                DateUtil.dateToFormatStr(startTime, TIME_FORMAT), DateUtil.dateToFormatStr(endTime, TIME_FORMAT));
        PayHistoryDTO.Result result = JsonUtil.parse(response, PayHistoryDTO.Result.class);
        if (result.getRetCode() != SUCCESS_RET_CODE) {
            return Collections.emptyList();
        }
        for (PayHistoryDTO payHistory : result.getInfoPayHistorys()) {
            payHistory.setFee(unify(payHistory.getFee()));
            payHistory.setCustomerMoney(unify(payHistory.getCustomerMoney()));
        }
        return result.getInfoPayHistorys();
    }

    /**
     * 账号充值
     *
     * @param apiUrl  VOS接口地址
     * @param account 账号
     * @param money   充值金额
     * @param comment 充值备注
     * @return 充值信息
     */
    public static PayDTO payCustomer(String apiUrl, String account, BigDecimal money, String comment) {
        // 充值对象名称类型 2:账户 6:平台话机 11:在用电话卡卡号 25:绑定号码 44:对接网关
        String response = request(apiUrl, ApiEnum.PAY_CUSTOMER, account, 2, money, comment);
        PayDTO.Result result = JsonUtil.parse(response, PayDTO.Result.class);
        if (result.getRetCode() != SUCCESS_RET_CODE) {
            return null;
        }
        return result.getInfoPay();
    }

    /**
     * 修改账户信息
     *
     * @param apiUrl     VOS接口地址
     * @param account    账号
     * @param name       账号名称
     * @param limitMoney 账户透支限额
     * @param lockType   锁定类型
     * @param validTime  账户有效期
     * @return 是否修改成功
     */
    public static boolean modifyCustomer(String apiUrl, String account, String name, BigDecimal limitMoney, Integer lockType, Date validTime) {
        Long paramValidTime = null;
        if (Objects.nonNull(validTime)) {
            paramValidTime = validTime.getTime();
        }
        String response = request(apiUrl, ApiEnum.MODIFY_CUSTOMER, account, name, limitMoney, lockType, paramValidTime);
        ResultDTO result = JsonUtil.parse(response, ResultDTO.class);
        return result.getRetCode() == SUCCESS_RET_CODE;
    }

    /**
     * 获取网关报表
     *
     * @param server    VOS服务器信息
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 报表数据
     */
    public static List<ReportGatewayFeeDTO> getGatewayReport(VosServer server, Date startDate, Date endDate, String... accounts) {
        // 每一天的数据都是一张表
        int days = DateUtil.daysOfTwoDate(startDate, endDate);
        try (Connection connection = DataSourceUtil.connect(server.getMysqlUrl(), server.getMysqlUser(), server.getMysqlPassword())) {
            List<ReportGatewayFeeDTO> reportList = new ArrayList<>();
            for (int i = 0; i < days; i++) {
                String targetSql = SqlEnum.SELECT_GATEWAY_FEE_REPORT.prepareSql(startDate, i, SqlUtil.toArrayStr(accounts));
                reportList.addAll(DataSourceUtil.selectList(connection, targetSql, ReportGatewayFeeDTO.class));
            }
            return reportList;
        } catch (Exception e) {
            log.error("VosHelper VOS Mysql query error", e);
        }
        return Collections.emptyList();
    }

    private static int parseQueryDayCount(Date startDate, Date endDate) {
        int days = DateUtil.daysOfTwoDate(startDate, endDate) + 1;
        return Math.min(days, MAX_QUERY_DAY);
    }


    public static List<CallDetailDTO> queryCallDetail(VosServer server, Date startDate, Date endDate, String phone) {
        int days = parseQueryDayCount(startDate, endDate);
        try (Connection connection = DataSourceUtil.connect(server.getMysqlUrl(), server.getMysqlUser(), server.getMysqlPassword())) {
            List<CallDetailDTO> detailList = new ArrayList<>();
            for (int i = 0; i < days; i++) {
                String targetSql = SqlEnum.SELECT_CDR_QUERY_BY_PHONE.prepareSql(startDate, i, phone);
                detailList.addAll(DataSourceUtil.selectList(connection, targetSql, CallDetailDTO.class));
            }
            return detailList;
        } catch (Exception e) {
            log.error("VosHelper VOS Mysql query error queryCallDetail :", e);
        }
        return Collections.emptyList();
    }

    public static List<CdrDTO> queryTodayCdrReport(VosServer server, int latestId) {
        Date nowDate = new Date();
        return queryCdrReport(server, DateUtil.getDateBegin(nowDate), DateUtil.getDateEnd(nowDate), latestId);
    }

    public static List<CdrDTO> queryCdrReport(VosServer server, Date day, int latestId) {
        return queryCdrReport(server, DateUtil.getDateBegin(day), day, latestId);
    }

    public static List<CdrDTO> queryCdrReport(VosServer server, Date startDate, Date endDate, int latestId) {
        final int querySize = 2000;
        final int loadLimit = 50000;
        int days = parseQueryDayCount(startDate, endDate);
        try (Connection connection = DataSourceUtil.connect(server.getMysqlUrl(), server.getMysqlUser(), server.getMysqlPassword())) {
            List<CdrDTO> cdrList = new ArrayList<>();
            for (int i = 0; i < days; i++) {
                int index = 0;
                int size = querySize;
                Date thisDate = DateUtil.getDateBeforeOrAfterDays(startDate, days);
                long startMs = i == 0 ? startDate.getTime() : DateUtil.getDateBegin(thisDate).getTime();
                long endMs = i == days - 1 ? endDate.getTime() : DateUtil.getDateEnd(thisDate).getTime();
                while (size == querySize) {
                    String targetSql = SqlEnum.SELECT_CDR_REPORT.prepareSql(startDate, i, index * size, size,
                            String.valueOf(latestId), String.valueOf(startMs), String.valueOf(endMs));
                    List<CdrDTO> limitCdrList = DataSourceUtil.selectList(connection, targetSql, CdrDTO.class);
                    size = limitCdrList.size();
                    index++;
                    cdrList.addAll(limitCdrList);
                    // 查询限制
                    if (loadLimit <= cdrList.size()) {
                        return cdrList;
                    }
                }
            }
            return cdrList;
        } catch (Exception e) {
            log.error("VosHelper VOS Mysql query error queryCdr :", e);
        }
        return Collections.emptyList();
    }


    private static String request(String apiUrl, ApiEnum apiEnum, Object... paramValues) {
        Map<String, Object> paramMap = new HashMap<>(paramValues.length);
        for (int i = 0; i < apiEnum.getParams().length; i++) {
            if (Objects.isNull(paramValues[i])) {
                continue;
            }
            paramMap.put(apiEnum.getParams()[i], paramValues[i]);
        }
        return HttpUtil.sendPost(apiUrl + apiEnum.getUri(), BASE_HEADER, paramMap, true);
    }

    private static BigDecimal unify(BigDecimal bigDecimal) {
        return bigDecimal.setScale(3, RoundingMode.HALF_UP);
    }


}
